package com.news.service.impl;

import com.news.model.ArticleImage;
import com.news.service.ImageCacheService;
import com.news.util.HttpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.net.URL;
import java.nio.file.*;
import java.security.MessageDigest;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * 图片缓存服务实现
 */
public class ImageCacheServiceImpl implements ImageCacheService {
    
    private static final Logger logger = LoggerFactory.getLogger(ImageCacheServiceImpl.class);
    
    private final String cacheDir;
    private final ConcurrentHashMap<String, String> urlToPathCache = new ConcurrentHashMap<>();
    private final Executor downloadExecutor = Executors.newFixedThreadPool(4);
    
    public ImageCacheServiceImpl() {
        // 在用户目录下创建缓存目录
        this.cacheDir = System.getProperty("user.home") + File.separator + ".news_cache" + File.separator + "images";
        initializeCacheDirectory();
    }
    
    public ImageCacheServiceImpl(String customCacheDir) {
        this.cacheDir = customCacheDir;
        initializeCacheDirectory();
    }
    
    private void initializeCacheDirectory() {
        try {
            Path cachePath = Paths.get(cacheDir);
            if (!Files.exists(cachePath)) {
                Files.createDirectories(cachePath);
                logger.info("图片缓存目录已创建: {}", cacheDir);
            }
        } catch (IOException e) {
            logger.error("创建缓存目录失败: {}", cacheDir, e);
        }
    }
    
    @Override
    public CompletableFuture<String> cacheImage(ArticleImage image) {
        if (image == null || !image.isValid()) {
            return CompletableFuture.completedFuture(null);
        }
        
        String imageUrl = image.getUrl();
        
        // 检查是否已缓存
        String cachedPath = getCachedImagePath(imageUrl);
        if (cachedPath != null) {
            image.setLocalPath(cachedPath);
            image.setLoaded(true);
            return CompletableFuture.completedFuture(cachedPath);
        }
        
        return CompletableFuture.supplyAsync(() -> {
            try {
                return downloadAndCacheImage(image);
            } catch (Exception e) {
                logger.error("缓存图片失败: {}", imageUrl, e);
                return null;
            }
        }, downloadExecutor);
    }
    
    @Override
    public CompletableFuture<Integer> cacheImages(List<ArticleImage> images) {
        if (images == null || images.isEmpty()) {
            return CompletableFuture.completedFuture(0);
        }
        
        return CompletableFuture.supplyAsync(() -> {
            int successCount = 0;
            for (ArticleImage image : images) {
                try {
                    String cachedPath = cacheImage(image).get();
                    if (cachedPath != null) {
                        successCount++;
                    }
                } catch (Exception e) {
                    logger.warn("缓存图片失败: {}", image.getUrl(), e);
                }
            }
            logger.info("批量缓存完成: 成功 {}/{} 张图片", successCount, images.size());
            return successCount;
        }, downloadExecutor);
    }
    
    @Override
    public String getCachedImagePath(String imageUrl) {
        if (imageUrl == null || imageUrl.trim().isEmpty()) {
            return null;
        }
        
        // 检查内存缓存
        if (urlToPathCache.containsKey(imageUrl)) {
            String cachedPath = urlToPathCache.get(imageUrl);
            if (Files.exists(Paths.get(cachedPath))) {
                return cachedPath;
            } else {
                urlToPathCache.remove(imageUrl);
            }
        }
        
        // 检查文件系统缓存
        String fileName = generateCacheFileName(imageUrl);
        Path filePath = Paths.get(cacheDir, fileName);
        
        if (Files.exists(filePath)) {
            String path = filePath.toString();
            urlToPathCache.put(imageUrl, path);
            return path;
        }
        
        return null;
    }
    
    @Override
    public boolean isImageCached(String imageUrl) {
        return getCachedImagePath(imageUrl) != null;
    }
    
    @Override
    public int cleanExpiredCache(int maxAgeHours) {
        int cleanedCount = 0;
        
        try {
            long cutoffTime = System.currentTimeMillis() - (maxAgeHours * 60 * 60 * 1000L);
            
            try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(cacheDir))) {
                for (Path file : stream) {
                    if (Files.isRegularFile(file)) {
                        long lastModified = Files.getLastModifiedTime(file).toMillis();
                        if (lastModified < cutoffTime) {
                            try {
                                Files.delete(file);
                                cleanedCount++;
                                
                                // 从内存缓存中移除
                                urlToPathCache.entrySet().removeIf(entry -> 
                                    entry.getValue().equals(file.toString()));
                                
                            } catch (IOException e) {
                                logger.warn("删除过期缓存文件失败: {}", file, e);
                            }
                        }
                    }
                }
            }
            
            logger.info("清理过期缓存完成: 删除 {} 个文件", cleanedCount);
            
        } catch (IOException e) {
            logger.error("清理过期缓存失败", e);
        }
        
        return cleanedCount;
    }
    
    @Override
    public void clearAllCache() {
        try {
            urlToPathCache.clear();
            
            try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(cacheDir))) {
                for (Path file : stream) {
                    if (Files.isRegularFile(file)) {
                        Files.delete(file);
                    }
                }
            }
            
            logger.info("所有缓存已清空");
            
        } catch (IOException e) {
            logger.error("清空缓存失败", e);
        }
    }
    
    @Override
    public long getCacheSize() {
        long totalSize = 0;
        
        try {
            try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(cacheDir))) {
                for (Path file : stream) {
                    if (Files.isRegularFile(file)) {
                        totalSize += Files.size(file);
                    }
                }
            }
        } catch (IOException e) {
            logger.error("计算缓存大小失败", e);
        }
        
        return totalSize;
    }
    
    @Override
    public int getCacheFileCount() {
        int count = 0;
        
        try {
            try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get(cacheDir))) {
                for (Path file : stream) {
                    if (Files.isRegularFile(file)) {
                        count++;
                    }
                }
            }
        } catch (IOException e) {
            logger.error("计算缓存文件数量失败", e);
        }
        
        return count;
    }
    
    /**
     * 下载并缓存图片
     */
    private String downloadAndCacheImage(ArticleImage image) throws IOException {
        String imageUrl = image.getUrl();
        String fileName = generateCacheFileName(imageUrl);
        Path filePath = Paths.get(cacheDir, fileName);
        
        // 如果文件已存在，直接返回
        if (Files.exists(filePath)) {
            String path = filePath.toString();
            urlToPathCache.put(imageUrl, path);
            image.setLocalPath(path);
            image.setLoaded(true);
            return path;
        }
        
        logger.info("开始下载图片: {}", imageUrl);
        
        try {
            // 使用HttpUtil下载图片
            byte[] imageData = downloadImageData(imageUrl);
            
            if (imageData != null && imageData.length > 0) {
                // 写入文件
                Files.write(filePath, imageData);
                
                // 更新图片信息
                image.setFileSize(imageData.length);
                image.setLocalPath(filePath.toString());
                image.setLoaded(true);
                
                // 缓存路径映射
                urlToPathCache.put(imageUrl, filePath.toString());
                
                logger.info("图片下载成功: {} -> {}", imageUrl, fileName);
                return filePath.toString();
                
            } else {
                logger.warn("下载的图片数据为空: {}", imageUrl);
                return null;
            }
            
        } catch (Exception e) {
            logger.error("下载图片失败: {}", imageUrl, e);
            throw new IOException("下载图片失败: " + e.getMessage(), e);
        }
    }
    
    /**
     * 下载图片数据
     */
    private byte[] downloadImageData(String imageUrl) throws IOException {
        try {
            // 使用现有的HttpUtil，但需要扩展支持二进制数据
            return downloadBinary(imageUrl);
        } catch (Exception e) {
            logger.error("下载图片数据失败: {}", imageUrl, e);
            throw new IOException("下载失败", e);
        }
    }
    
    /**
     * 下载二进制数据
     */
    private byte[] downloadBinary(String url) throws IOException {
        try {
            URL urlObj = new URL(url);
            try (InputStream inputStream = urlObj.openStream();
                 ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
                
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    outputStream.write(buffer, 0, bytesRead);
                }
                
                return outputStream.toByteArray();
            }
        } catch (Exception e) {
            throw new IOException("下载二进制数据失败: " + url, e);
        }
    }
    
    /**
     * 生成缓存文件名
     */
    private String generateCacheFileName(String imageUrl) {
        try {
            // 使用MD5生成唯一文件名
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] hash = md.digest(imageUrl.getBytes("UTF-8"));
            
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            
            // 尝试获取文件扩展名
            String extension = getFileExtension(imageUrl);
            
            return hexString.toString() + (extension.isEmpty() ? ".jpg" : "." + extension);
            
        } catch (Exception e) {
            logger.error("生成缓存文件名失败", e);
            // 后备方案：使用时间戳
            return "img_" + System.currentTimeMillis() + ".jpg";
        }
    }
    
    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String url) {
        try {
            // 移除查询参数
            int queryIndex = url.indexOf('?');
            if (queryIndex > 0) {
                url = url.substring(0, queryIndex);
            }
            
            int lastDot = url.lastIndexOf('.');
            int lastSlash = url.lastIndexOf('/');
            
            if (lastDot > lastSlash && lastDot < url.length() - 1) {
                String ext = url.substring(lastDot + 1).toLowerCase();
                // 验证是否为有效的图片扩展名
                if (ext.matches("jpg|jpeg|png|gif|webp|svg|bmp")) {
                    return ext;
                }
            }
        } catch (Exception e) {
            logger.debug("获取文件扩展名失败: {}", url);
        }
        
        return "jpg"; // 默认扩展名
    }
} 